﻿#region Licence
// Copyright (c) 2011 BAX Services Bartosz Adamczewski
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CSpec.Testing;
using CSpec.Tests.Examples.Calculator;
using CSpec.Extensions;

namespace CSpec.Tests.ExamplesSpec.Calculator
{

    /*
     * This test serves as a Guide how to do testing in Cspec
     * 
     * 
     */

    /// <summary>
    /// Spec type to a Calculator project
    /// </summary>
    /// <remarks>
    /// You don't need to use an interface type to use
    /// CSpec regullar Calc class would be suficient,
    /// but when using the interface type, you gent additional
    /// tags like @raise, @not_raise.
    /// 
    /// Why is it so?
    /// 
    /// Only interface Types give the ability to do at as they can
    /// be Proxied, thus advanced dynamic operations are possible
    /// in this context we can do (pseudo) backwards lookahead.
    /// </remarks>
    public class CalcSpec : CSpecFacade<ICalc>
    {
        /// <summary>
        /// Initializes the parametric constructor of Cspec
        /// and passes the constructor of the Specd class
        /// </summary>
        /// <remarks>
        /// You can use the default constructor if the creation of the parametric one is a chore
        /// (when your class doesn't have simple constructors but require tons of operations to be created),
        /// and just call InitializeFacade
        /// </remarks>
        public CalcSpec()
            : base(new Calc())
        {
        }

        /// <summary>
        /// Clean the Total after each operaton.
        /// </summary>
        protected override void  BeforeOperation()
        {
 	        ObjSpec.Total = 0;
        }

        //MS aparently has a bug in the code.
        //without this nothing will work and the DESCRIBE ALL delegate will throw exception.
        //In mono it works perfect.
        private Action<string> dummy = null;

        /// <summary>
        /// Simple addition testing
        /// </summary>
        DescribeAll describe_sum =
            (@it, @do) =>
            {
                @it("Adds a value of 5 to total");
                @do.Sum(5).Should(@be => 5);
             
            };

        /// <summary>
        /// Simple addition testing
        /// </summary>
        [It("Adds a value of 5 to total but using it attribute, instead of tag")]
        Describe describe_sum_attr =
            (@do) =>
            {
                @do.Sum(5).Should(@be => 5);
            };


        /// <summary>
        /// Simple substraction testing
        /// </summary>
        DescribeAll describe_sub =
            (@it, @do) =>
            {
                @it("Substracts a value of 5 from total");
                @do.Sub(5).Should(@be => -5);
            };

        /// <summary>
        /// Simple power testing
        /// </summary>
        /// <remarks>
        /// There is a catch here AHA!, the result of 
        /// calling POW needs to be converted to int,
        /// in this context this is simple but you have to watch for this.
        /// </remarks>
        DescribeAll describe_pow =
            (@it, @do) =>
            {
                @it("Takes the total to a power of 5");
                @do.Total = 5;
                @do.Pow(5).Should(@be => (int)Math.Pow(5,5));
            };

        /// <summary>
        /// Testing of Div, and expecting exceptions
        /// </summary>
        DescribeAll describe_div =
            (@it, @do) =>
            {
                @it("Divides the total by a value of 5");
                @it("In this context Divide should be ok");
                @do.Div(5).Should(@not_raise => typeof(DivideByZeroException));

                @it("Divides the total by a value of 0");
                @it("In this context Divide should be wrong and throw an exception");
                @do.Div(0).Should(@raise => typeof(DivideByZeroException));
            };

        /// <summary>
        /// Testing the value that needs to fit some range.
        /// </summary>
        DescribeAll describe_range =
            (@it, @do) =>
            {
                Random rnd = new Random();
                var value = rnd.Next(1, 5);

                @it("Adds a random value from 1 to 5, current value: " + value);
                @it("In this case the total should be in a specified range");

                @do.Sum(value);
                @do.Total.Should(@be_close => 1, 5); 
            };

        
        // This method wrapper is actually doing nothing, so it would
        // seem useless, but it's very needed as there is a BUG IN THE PLATFORM
        // and without calling something that will shadow our delegate we get
        // invalid program exception. NET has a huge collection of bugs
        // regarding anonymous methods, so watch out.
        
        /// <summary>
        /// Testing the certain flow of the methods.
        /// </summary>
        DescribeAll describe_flow =
            (@it, @do) =>
            {
                @it("Executes a certain flow of Calc methods:");
                @it("Sum(1), Sub(1)");
                @it("In this case we will ask if Sum was executed");

                @do.Sum(1);
                @do.Sub(1);
                @do.Should(@have_called => @do.Method((Func<int,int>)@do.Sum));

            };

        /// <summary>
        /// Testing instance check.
        /// </summary>
        DescribeAll describe_instance =
            (@it, @do) =>
            {
                @it("Tests if the return value of sum has the specified instance");
                @do.Sum(1).Should(@be_instance_of => typeof(int));
            };
    }
}
